package com.mathdroid.gui.main.encounter;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import com.mathdroid.gui.ImageLoader;
import com.mathdroid.gui.main.DroidComboBoxModel;
import com.mathdroid.gui.main.EGlobalStrings;
import com.mathdroid.model.EModelObserverEvent;
import com.mathdroid.model.IModelObserver;
import com.mathdroid.model.Model;
import com.mathdroid.model.encounters.EncounterTemplate;
import com.mathdroid.model.encounters.EncounterTemplates;
import com.mathdroid.persistence.Serializer;

/**
 * This panel gives the option to edit the encounter settings, 
 * load and save templates.
 */
public final class EncounterPanel extends JPanel implements IModelObserver {
	/**
	 * Default UID.
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * The encounter templates.
	 */
	private JComboBox templates = null;
	
	/**
	 * Button to save a template.
	 */
	private final JButton save = new JButton();
	
	/**
	 * Button to delete a template.
	 */
	private final JButton delete = new JButton();
	
	/**
	 * UI component constraints.
	 */
	private final GridBagConstraints c = new GridBagConstraints();
	
	/**
	 * The width of the grid.
	 */
	private static final int GRID_WIDTH = 3;
	
	/**
	 * The spacing between components.
	 */
	private static final int SPACING = 10;
	
	/**
	 * The width of the save/delete button.
	 */
	private static final int BUTTON_WIDTH = 25;
	
	/**
	 * The height of the save/delete button.
	 */
	private static final int BUTTON_HEIGHT = 25;
	
	/**
	 * The weight of "heavy" components.
	 */
	private static final int HEAVY_WEIGHT = 10;
	
	/**
	 * The weight of "huge" components.
	 */
	private static final int HUGE_WEIGHT = 100;
	
	/**
	 * The serializer.
	 */
	private Serializer s = new Serializer();
	
	/**
	 * The image loader.
	 */
	private final ImageLoader il = new ImageLoader();
	
	/**
	 * This is a panel inside this panel. It is necessary to have this because
	 * of the use of BorderLayout. The content should be in the NORTH, not in
	 * the center.
	 */
	private final JPanel content = new JPanel();
	
	/**
	 * Textfield for the duration of the encounter.
	 */
	private final JTextField txtMinutes = new JTextField();
	
	/**
	 * Textfield for the duration of the encounter.
	 */
	private final JTextField txtSeconds = new JTextField();
	
	/**
	 * Standard constructor to create the encounters panel.
	 */
	public EncounterPanel() {
		Model.registerObserver(this);
		this.setLayout(new BorderLayout());
		this.content.setLayout(new GridBagLayout());
		this.buildComboBox();
		this.buildSaveButton();
		this.buildDeleteButton();
		this.buildDuration();
		this.add(this.content, BorderLayout.NORTH);
		
		this.templates.configureEditor(
				templates.getEditor(), 
				Model.getProfile().getEncounter().toString());
	}

	/**
	 * Build the textfield for the encounter duration.
	 */
	private void buildDuration() {
		final JLabel minutes = new JLabel("Duration (min:sec)");
		final JLabel slash = new JLabel(":");
		final JPanel dur = new JPanel();
		dur.setLayout(new GridBagLayout());
		
		DocumentListener d = new DocumentListener() {
			@Override
			public void changedUpdate(final DocumentEvent arg0) {
				refreshModel();
			}
			@Override
			public void insertUpdate(final DocumentEvent arg0) {
				refreshModel();
			}
			@Override
			public void removeUpdate(final DocumentEvent arg0) { 
				refreshModel();
			}
		};
		
		Border b = BorderFactory.createTitledBorder("Settings");
		dur.setBorder(b);
		
		this.c.fill = GridBagConstraints.HORIZONTAL;
		this.c.gridwidth = 1;
		this.c.gridx = 0;
		this.c.gridy = 0;
		this.c.weightx = HEAVY_WEIGHT;
		this.c.insets = new Insets(SPACING, SPACING, SPACING, SPACING);
		dur.add(minutes, this.c);
		
		final Dimension txtDim = new Dimension(75, 25);
		
		this.c.gridx = 1;
		this.c.insets = new Insets(SPACING, 0, SPACING, SPACING);
		this.c.weightx = HEAVY_WEIGHT;
		this.txtMinutes.getDocument().addDocumentListener(d);
		this.txtMinutes.setPreferredSize(txtDim);
		dur.add(this.txtMinutes, this.c);
		
		this.c.gridx = 2;
		this.c.weightx = 1;
		dur.add(slash, this.c);
		
		this.c.gridx = 3;
		this.c.weightx = HEAVY_WEIGHT;
		this.txtSeconds.getDocument().addDocumentListener(d);
		this.txtSeconds.setPreferredSize(txtDim);
		dur.add(this.txtSeconds, this.c);
		
		this.c.fill = GridBagConstraints.HORIZONTAL;
		this.c.gridwidth = GRID_WIDTH;
		this.c.gridx = 0;
		this.c.gridy = 1;
		this.c.weightx = 1;
		this.c.insets = new Insets(0, SPACING, SPACING, SPACING);
		this.content.add(dur, this.c);
		
		this.refresh();
	}

	/**
	 * Refresh the model.
	 */
	protected void refreshModel() {
		EncounterTemplate t = Model.getProfile().getEncounter();
		double mins = 0;
		double secs = 0;
		
		try {
			this.txtMinutes.setBackground(Color.WHITE);
			mins = Double.valueOf(this.txtMinutes.getText());
		} catch (Exception e) {
			this.txtMinutes.setBackground(Color.RED);
		}
		
		try {
			this.txtSeconds.setBackground(Color.WHITE);
			secs = Double.valueOf(this.txtSeconds.getText());
		} catch (Exception e) {
			this.txtSeconds.setBackground(Color.RED);
		}
		
		final int secondInMillies = 1000;
		final int minuteInMillies = 60000;
		t.setDurationMs(mins * minuteInMillies + secs * secondInMillies);
		
		Model.encounterChanged();
	}

	/**
	 * Build the button to delete templates.
	 */
	private void buildDeleteButton() {
		this.delete.setToolTipText(EGlobalStrings.DELETE_TEMPLATE.toString());
		this.delete.setPreferredSize(
				new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT));
		this.delete.setIcon(this.il.getImage("delete"));
		this.c.fill = GridBagConstraints.NONE;
		this.c.gridx = 2;
		this.c.gridy = 0;
		this.c.weightx = 1;
		this.c.insets = new Insets(SPACING, 0, SPACING, SPACING);
		this.content.add(this.delete, this.c);	
		this.delete.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				Object o = templates.getSelectedItem();
				if (o instanceof EncounterTemplate) {
					EncounterTemplates t = s.getEncounterTemplates();
					t.remove(((EncounterTemplate) o).toString());
					s.serialize(t);
					templates.removeItem(o);
					templates.configureEditor(templates.getEditor(), null);
				}
			}
		});
	}

	/**
	 * Build the button to save a template.
	 */
	private void buildSaveButton() {
		this.save.setToolTipText(EGlobalStrings.SAVE_TEMPLATE.toString());
		this.save.setPreferredSize(
				new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT));
		this.save.setIcon(this.il.getImage("add"));
		this.c.fill = GridBagConstraints.NONE;
		this.c.gridx = 1;
		this.c.gridy = 0;
		this.c.weightx = 1;
		this.c.insets = new Insets(SPACING, 0, SPACING, SPACING);
		this.content.add(this.save, this.c);	
		this.save.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				saveTemplate();
				refreshTemplates();
			}
		});
	}

	/**
	 * Build the templates combo box.
	 */
	private void buildComboBox() {
		this.templates = new JComboBox(new DroidComboBoxModel());
		EncounterTemplates b = this.s.getEncounterTemplates();
		
		for (EncounterTemplate i : b.values()) {
			this.templates.addItem(i);
		}
		
		this.templates.setEditable(true);
		this.c.fill = GridBagConstraints.HORIZONTAL;
		this.c.gridx = 0;
		this.c.gridy = 0;
		this.c.weightx = HUGE_WEIGHT;
		this.c.insets = new Insets(SPACING, SPACING, SPACING, SPACING);
		this.content.add(this.templates, this.c);
		this.templates.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				Object o = templates.getSelectedItem();
				if (o instanceof EncounterTemplate) {
					delete.setEnabled(true);
					Model.setEncounter((EncounterTemplate) o);
					refreshTemplates();
				}
				
			}
		});
		
		this.templates.getEditor().getEditorComponent()
				.addKeyListener(new KeyListener() {
			@Override
			public void keyPressed(final KeyEvent e) { 
				delete.setEnabled(false);
				
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					saveTemplate();
					refreshTemplates();
				}
			}
			@Override
			public void keyReleased(final KeyEvent arg0) { }
			@Override
			public void keyTyped(final KeyEvent arg0) { }
		});
	}

	/**
	 * Save the template to the persistence.
	 */
	private void saveTemplate() {
		Object o = templates.getEditor().getItem();
		EncounterTemplates t = s.getEncounterTemplates();
		EncounterTemplate e = Model.getProfile().getEncounter();
		String name = e.toString();
		boolean valid = true;
		
		if (o instanceof String) {
			name = (String) o;
			if (name.equals("")) {
				valid = false;
			}
		}
		
		if (valid) {
			e.setName(name);
			
			/* Put the template into the persistence. */
			t.put(e.toString(), e);
			s.serialize(t);
			
			this.delete.setEnabled(true);
			
			try {
				JOptionPane.showMessageDialog(Model.getDroidFrame(),
						"Saved encounter template \"" + name + "\".\n"
							+ "Encounter templates are shared between all "
							+ "profiles.",
						"Template saved", 
						JOptionPane.INFORMATION_MESSAGE);
				
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						templates.configureEditor(
							templates.getEditor(), 
							Model.getProfile().getEncounter().toString());
					}
				});
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	/**
	 * Refresh the combo box model.
	 */
	private void refreshTemplates() {
		EncounterTemplates t;
		templates.removeAllItems();
		t = s.getEncounterTemplates();
		for (EncounterTemplate temp : t.values()) {
			templates.addItem(temp);
		}
	}

	@Override
	public void modelChanged(final EModelObserverEvent e) {
		if (EModelObserverEvent.PROFILE_LOADED == e 
				|| e == EModelObserverEvent.ENCOUNTER_LOADED) {
			this.refresh();
		}
	}

	/**
	 * Refresh the view.
	 */
	private void refresh() {
		String seconds = "" + Model.getProfile().getEncounter().getSeconds();
		String minutes = "" + Model.getProfile().getEncounter().getMinutes();
		this.txtSeconds.setText(seconds);
		this.txtMinutes.setText(minutes);
	}

}
